package cn.pugle.mianshi.ali20180125;


import java.util.*;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 题目一：
 * 同时有N个商品（用long类型的商品id表示），
 * 每个商品都可以被任何一个用户（long类型的用户id）订阅，每被订阅一次，该商品的订阅数加1，
 * 同一个用户对同一个商品只能订阅一次。
 * +编辑写一个类，用3个方法提供以下功能（这3个方法都要保证线程安全）：
 * 1）为指定的用户id订阅指定的商品id+
 * 2)+返回所有商品的订阅总数+
 * 3）根据商品ID返回这个商品的订阅数
 *
 * @author tzp
 * @since 2018/1/25
 */
public class GoodsManager {
    /**
     * 商品id - 订阅用户ids
     */
    private Map<Long, Set<Long>> goodsMap;
    private volatile Long totalOrdered = 0L;

    public static void main(String[] args) throws Exception {
        GoodsManager manager = new GoodsManager();
        manager.initGoods(new ArrayList<Long>() {{
            add(0L);
            add(1L);
            add(2L);
            add(3L);
            add(4L);
        }});
//        manager.orderGoods(1L, 1001L);
//        manager.orderGoods(1L, 1001L);
//        manager.orderGoods(1L, 1001L);
//        manager.orderGoods(3L, 1001L);
//        manager.orderGoods(5L, 1001L);
        ExecutorService executorService = Executors.newFixedThreadPool(4);
        executorService.submit(new RandomOrderRunner(manager));
        executorService.submit(new RandomOrderRunner(manager));
        executorService.submit(new RandomOrderRunner(manager));

        Thread.sleep(5000);
        System.out.println("Total:" + manager.totalOrderedNum());
        System.out.println("ByGoods: " + manager.orderedNumByGoods(1L));
    }

    /**
     * 初始化商品列表
     */
    public void initGoods(List<Long> goodsIds) {
        goodsMap = new HashMap<>(goodsIds.size());
//        goodsIds.forEach(id -> goodsMap.put(id, new ConcurrentSkipListSet<>()));
        goodsIds.forEach(id -> goodsMap.put(id, new HashSet<>()));
    }

    /**
     * 订阅商品
     * 返回是否订阅成功
     */
    public boolean orderGoods(Long goodsId, Long userId) {
        Set<Long> userIdSet = goodsMap.get(goodsId);
        if (userIdSet == null) {
            return false;
        } else {
            boolean addSuccess = userIdSet.add(userId);
            if (addSuccess) {
                totalOrdered++;
                return true;
            }
            return false;
        }
    }

    public Long totalOrderedNum() {
        return totalOrdered;
    }

    public Integer orderedNumByGoods(Long goodsId) throws Exception {
        Set<Long> userIdSet = goodsMap.get(goodsId);
        if (userIdSet == null) {
            throw new Exception("Goods do not exists");
        } else {
            return userIdSet.size();
        }
    }

    public static class RandomOrderRunner implements Runnable {
        private GoodsManager manager;

        public RandomOrderRunner(GoodsManager manager) {
            this.manager = manager;
        }

        @Override
        public void run() {
            for (int i = 0; i < 4; i++) {
                long randomGoods = new Double(Math.random() * 5).longValue();
                long randomUser = new Double(1000 + Math.random() * 5).longValue();
                System.out.println(randomGoods + " byUser " + randomUser);
                manager.orderGoods(randomGoods, randomUser);
            }
        }
    }
}